#include "D3D11Pad.h"
#include "Texture.h"

struct FontCache {
	int height;
	int orient;
	bool prop;
	char face[32];
	oapi::Font::Style style;
	class D3D11Text *pFont;
	HFONT hFont;
} fcache[128];

int nfcache = 0;
int pens_allocated = 0;
int brushes_allocated = 0;
int fonts_allocated = 0;

oapi::Font *def_Font = NULL;
oapi::Pen *def_Pen = NULL;

#pragma region static
ID3D11InputLayout
	*D3D11Pad::IL_D3DXVECTOR3	= NULL;

ID3D11Buffer
	*D3D11Pad::Circle_low		= NULL,
	*D3D11Pad::Circle_high		= NULL,
	*D3D11Pad::cb_VS			= NULL,
	*D3D11Pad::cb_PS			= NULL,
	*D3D11Pad::VB				= NULL,
	*D3D11Pad::IB				= NULL;

ID3D11VertexShader
	*D3D11Pad::VS_Ellipse		= NULL,
	*D3D11Pad::VS_WideRect		= NULL,
	*D3D11Pad::VS_Line			= NULL,
	*D3D11Pad::VS_Fill			= NULL;

ID3D11PixelShader
	*D3D11Pad::PS_Draw			= NULL,
	*D3D11Pad::PS_Fill			= NULL;

const UINT
	D3D11Pad::SkpStride = 12,
	D3D11Pad::VBOffset = 0;

UINT
	D3D11Pad::vb_size = 34,
	D3D11Pad::ib_size = 96;

const float	D3D11Pad::BlendFactors[4] = { 1.0f };

void D3D11Pad::GlobalInit() {
//shaders:
	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	D3D11_INPUT_ELEMENT_DESC ldesc[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 } };

	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Ellipse", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( ldesc, 1, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_D3DXVECTOR3 ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Ellipse ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_WideRect", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_WideRect ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Line", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Line ) );
	REL( SBlob );
	REL( EBlob );
	
	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "VS_Fill", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Fill ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "PS_Draw", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Draw ) );
	REL( SBlob );
	REL( EBlob );

	CompileFromFile( "Modules\\D3D11Shaders\\D3D11Pad.fx", NULL, NULL, "PS_Fill", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Fill ) );
	REL( SBlob );
	REL( EBlob );

//buffers:
	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = 34*12;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VB ) );
	vb_size = 34;

	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = 2*32*3;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &IB ) );
	ib_size = 96;

//Circle buffers
	float angle = 0.0f, step = (float)PI2 / 15.0f;
	D3DXVECTOR3 Vtx[65];
	UINT j;

//Circle_low:
	Vtx[0] = D3DXVECTOR3( 0.5f, 0.5f, 0.0f );
	for( j = 1; j < 17; j++ ) {
		Vtx[j].x = sin( angle )*0.5f + 0.5f;
		Vtx[j].y = cos( angle )*0.5f + 0.5f;
		Vtx[j].z = angle;
		angle += step;
	}

//	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = 17*12;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sdata;
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &Circle_low ) );

//Circle_high:
	angle = 0.0f;
	step = (float)PI2 / 63.0f;

	Vtx[0] = D3DXVECTOR3( 0.5f, 0.5f, 0.0f );
	for( j = 1; j < 65; j++ ) {
		Vtx[j].x = sin( angle )*0.5f + 0.5f;
		Vtx[j].y = cos( angle )*0.5f + 0.5f;
		Vtx[j].z = angle;
		angle += step;
	}

	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = 65*12;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &Circle_high ) );

//constant buffer:
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 80;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &cb_VS ) );

	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 32;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &cb_PS ) );
}

void D3D11Pad::GlobalExit() {
	REL( IL_D3DXVECTOR3 );
	REL( VB );
	REL( IB );
	REL( VS_Ellipse );
	REL( VS_WideRect );
	REL( VS_Line );
	REL( VS_Fill );
	REL( PS_Draw );
	REL( PS_Fill );
	REL( Circle_low );
	REL( Circle_high );
	REL( cb_VS );
	REL( cb_PS );
	if (nfcache)
	{
		for(auto i = 0; i < nfcache; i++)
		{
			if (fcache[i].pFont)
			{
				delete fcache[i].pFont;
			}
		}
		nfcache = 0;
	}
}
#pragma endregion vertex buffers, shaders, layouts...

D3D11Pad::D3D11Pad( SURFHANDLE surf, TextureMgr * textureMgr, bool _static ) : oapi::Sketchpad( surf ) 
{
	m_textureMgr = textureMgr;

	cfont = NULL;
	cpen = NULL;
	cbrush = NULL;

	TEX = (Texture*)surf;
	if( TEX ) {		//surface
		Tex = TEX->GetTex();
		RTV_Tex = *TEX->GetRTV();
		TEX->GetSize( &width, &height );
	}
	else {			//2D buffer
		Tex = SC->PBuffer;
		RTV_Tex = SC->PBuffer_RTV;
		width = cfg->cWidth;
		height = cfg->cHeight;
	}
	iCtx->OMSetRenderTargets( 1, &RTV_Tex, NULL );
	D3DXMatrixOrthoOffCenterLH( &VSCB.M, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1.0f );

	D3D11_VIEWPORT VP;
    VP.Width = (float)width;
    VP.Height = (float)height;
    VP.MinDepth = 0.0f;
    VP.MaxDepth = 1.0f;
    VP.TopLeftX = 0;
    VP.TopLeftY = 0;
	iCtx->RSSetViewports( 1, &VP );	

	origx = 1;
	origy = 1;
	cx = cy = 0;
	BkMode = TRANSPARENT;
	HAlign = TA_LEFT;
	VAlign = TA_TOP;

	TextColor =		D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
	PenColor =		D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
	BrushColor =	D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f );
	BkColor =		D3DXCOLOR( 0.0f, 0.0f, 0.0f, 1.0f );

	cfont = def_Font;

	hDC = NULL;
}

D3D11Pad::~D3D11Pad() 
{
	//nothing to do
	if( hDC ) {
		SURFHANDLE surf = this->GetSurface();
		m_textureMgr->ReleaseSurfaceDC( hDC, (Texture*)surf );
		hDC = NULL;
	}
}

oapi::Font *D3D11Pad::SetFont( oapi::Font *font ) const
{
	oapi::Font *pfont = cfont;
	if( font )		cfont = font;
	else			cfont = def_Font;
	return pfont;
}

oapi::Pen *D3D11Pad::SetPen( oapi::Pen *pen ) const
{
	oapi::Pen *ppen = cpen;
	if( pen )		cpen = pen;
	else			cpen = NULL;
	if( cpen )
		PenColor = ((D3D11PadPen*)cpen)->fcolor;
	return ppen;
}

oapi::Brush *D3D11Pad::SetBrush( oapi::Brush *brush ) const
{
	oapi::Brush *pbrush = cbrush;
	cbrush = brush;
	if( brush )		BrushColor = ((D3D11PadBrush*)cbrush)->fcolor;
	else			BrushColor = D3DXCOLOR( (DWORD)0 );
	return pbrush;
}

void D3D11Pad::SetTextAlign( TAlign_horizontal TA_h, TAlign_vertical TA_v ) {
	HAlign = VAlign = 0;

	switch( TA_h ) {
	case LEFT:
		HAlign |= TA_LEFT;
		break;
	case CENTER:
		HAlign |= TA_CENTER;
		break;
	case RIGHT:
		HAlign |= TA_RIGHT;
		break;
	}

	switch( TA_v ) {
	case TOP:
		VAlign |= TA_TOP;
		break;
	case BASELINE:
		VAlign |= TA_BASELINE;
		break;
	case BOTTOM:
		VAlign |= TA_BOTTOM;
		break;
	}
}

DWORD D3D11Pad::SetTextColor( DWORD color ) {
	if( !(color & 0xFF000000) )
		color |= 0xFF000000;
	D3DXCOLOR prev = TextColor;
	TextColor = D3DXCOLOR( color );
	D3DXCOLORSWAP( &TextColor );
	D3DXCOLORSWAP( &prev );
	return prev;
}

DWORD D3D11Pad::SetBackgroundColor( DWORD color ) {
	if( !(color & 0xFF000000) )
		color |= 0xFF000000;
	D3DXCOLOR prev = BkColor;
	BkColor = D3DXCOLOR( color );
	D3DXCOLORSWAP( &BkColor );
	D3DXCOLORSWAP( &prev );
	return prev;
}

void D3D11Pad::SetBackgroundMode( BkgMode bk_mode ) {
	switch( bk_mode ) {
	case BK_TRANSPARENT:
		BkMode = TRANSPARENT;
		break;
	case BK_OPAQUE:
		BkMode = OPAQUE;
		break;
	}
}

DWORD D3D11Pad::GetCharSize() {
	TEXTMETRICA tm;
	if( !cfont )		return 0;
	((D3D11PadFont*)cfont)->pFont->GetD3D11TextMetrics( &tm );
	return MAKELONG( tm.tmHeight - tm.tmInternalLeading, tm.tmAveCharWidth );
}

DWORD D3D11Pad::GetTextWidth( const char *str, int len ) {
	if( !cfont )		return 0;
	return DWORD( ((D3D11PadFont*)cfont)->pFont->Length2( str, len ) );
}

void D3D11Pad::SetOrigin( int x, int y ) {
	origx = x;
	origy = y;
}

bool D3D11Pad::HasPen() {
	if( !cpen )		return false;
	if( ((D3D11PadPen*)cpen)->style == PS_NULL )	return false;
	return true;
}

bool D3D11Pad::IsDashed() {
	if( !cpen )		return false;
	if( ((D3D11PadPen*)cpen)->style == PS_DOT )		return true;
	return false;
}

bool D3D11Pad::HasWidePen() {
	if( !cpen )		return false;
	if( ((D3D11PadPen*)cpen)->width > 1 )	return true;
	return false;
}

bool D3D11Pad::HasBrush() {
	return (cbrush != NULL);
}

bool D3D11Pad::Text( int x, int y, const char *str, int len ) {
	if( !cfont )		return false;

	D3D11Text *pText = ((D3D11PadFont*)cfont)->pFont;

	switch( HAlign ) {
	default:
	case TA_LEFT:
		pText->SetTextHAlign( 0 );
		break;
	case TA_CENTER:
		pText->SetTextHAlign( 1 );
		break;
	case TA_RIGHT:
		pText->SetTextHAlign( 2 );
		break;
	}

	switch( VAlign ) {
	default:
	case TA_TOP:
		pText->SetTextVAlign( 0 );
		break;
	case TA_BASELINE:
		pText->SetTextVAlign( 1 );
		break;
	case TA_BOTTOM:
		pText->SetTextVAlign( 2 );
		break;
	}

/*	if( hDC ) {
		HFONT hFont = (HFONT)SelectObject( hDC, ((D3D11PadFont*)pText)->hFont );
		SetBkMode( hDC, BkMode );
		::SetTextAlign( hDC, TA_LEFT );
		SetBkColor( hDC, (COLORREF)0 );
		TextOutA( hDC, x, y, str, len );
		return true;
	}*/

	pText->SetRotation( ((D3D11PadFont*)cfont)->rotation );

	if( BkMode == OPAQUE )		pText->Print( &TextColor, origx + x - 1, origy + y - 1, str, len, &VSCB.M, &BkColor );
	else						pText->Print( &TextColor, origx + x - 1, origy + y - 1, str, len, &VSCB.M );

	return true;
}

void D3D11Pad::Pixel( int x, int y, DWORD col ) {
	if( x > (int)width-1 || y > (int)height-1 )
		return;

	D3DXCOLOR Color( col );
	D3DXCOLORSWAP( &Color );

	iCtx->ClearRenderTargetView( RTV_Tex_2x2, Color );

	D3D11_BOX Box;
	Box.back = 1;
	Box.front = 0;
	Box.top = 0;
	Box.left = 0;
	Box.right = 1;
	Box.bottom = 1;

	iCtx->CopySubresourceRegion( Tex, 0, x, y, 0, Tex_2x2, 0, &Box );
}

void D3D11Pad::MoveTo( int x, int y ) {
	cx = origx + x;
	cy = origy + y;
}

void D3D11Pad::LineTo( int x, int y ) {
	if( !HasPen() )	return;

	D3DXVECTOR3 pts[2];
	pts[0] = D3DXVECTOR3( (float)cx, (float)cy, 0.0f );
	pts[1] = D3DXVECTOR3( float(origx + x), float(origy + y), 0.0f );
	Lines( pts, 1 );
	cx = origx + x;
	cy = origy + y;
}

void D3D11Pad::Line( int x0, int y0, int x1, int y1 ) {
	if( !HasPen() )	return;

	x0 += origx;
	y0 += origy;
	x1 += origx;
	y1 += origy;
	D3DXVECTOR3 pts[2];
	pts[0] = D3DXVECTOR3( (float)x0, (float)y0, 0.0f );
	pts[1] = D3DXVECTOR3( (float)x1, (float)y1, 0.0f );
	Lines( pts, 1 );
	cx = x1;
	cy = y1;
}

void D3D11Pad::Rectangle( int l, int t, int r, int b ) {
	l += origx;
	t += origy;
	r += origx;
	b += origy;
	r--;
	b--;
	D3D11PadPen *pen = (D3D11PadPen*)cpen;
	if( HasWidePen() )	Rectangle3( float(l+r)*0.5f, float(t+b)*0.5f, float(r-l), float(b-t), pen->width );
	else				Rectangle2( float(l), float(t), float(r), float(b) );
}

void D3D11Pad::Ellipse( int l, int t, int r, int b ) {
	l += origx;
	t += origy;
	r += origx;
	b += origy;
	r--;
	b--;
	Ellipse2( float(l), float(t), float(r), float(b) );
}

void D3D11Pad::Polygon( const oapi::IVECTOR2 *pt, int npt ) {
	if( npt < 3 )	return;

	bool bIB = false;
	int nIdx = 0, j;

	WORD *Idx = NULL;
	D3DXVECTOR3 *Vtx = new D3DXVECTOR3 [npt+1];
	for( j = 0; j < npt; j++ )
		Vtx[j].x = float( pt[j].x + origx ),	Vtx[j].y = float( pt[j].y + origy );

	Vtx[npt].x = float( pt[0].x + origx ),	Vtx[npt].y = float( pt[0].y + origy );

	if( IsDashed() ) {
		Vtx[0].z = 0.0f;
		for( j = 1; j < npt; j++ ) {
			D3DXVECTOR3 d = Vtx[j] - Vtx[j-1];
			d.z = 0.0f;
			Vtx[j].z = Vtx[j-1].z + D3DXVec3Length( &d );
		}
		PSCB.Dash = 1;
	}
	else
		PSCB.Dash = 0;

	if( HasBrush() ) {
		Idx = new WORD [(npt-2)*3];
		nIdx = CreatePolyIndexList( Vtx, npt, Idx );
		if( nIdx > (int)ib_size )
			ExtendIndexBuffer( nIdx );
	}

	if( (npt+1) > (int)vb_size )
		ExtendVertexBuffer( npt+1 );

	D3D11_MAPPED_SUBRESOURCE SRes;
	ZeroMemory( &SRes, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, Vtx, (npt+1)*12 );
	iCtx->Unmap( VB, 0 );
	
	iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
	iCtx->VSSetShader( VS_Line, NULL, NULL );
	iCtx->PSSetShader( PS_Draw, NULL, NULL );

	SetRParams();

	if( nIdx ) {	//indexed triangles
		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

		iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, Idx, nIdx*2 );
		iCtx->Unmap( IB, 0 );

		PSCB.COL = BrushColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
		iCtx->DrawIndexed( nIdx, 0, 0 );
	}

	if( HasPen() ) {	//line
		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );

		PSCB.COL = PenColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->Draw( npt+1, 0 );
	}

	if( Idx )	delete [ ] Idx;
	delete [ ] Vtx;
}

void D3D11Pad::ExtendVertexBuffer( UINT new_size ) {
	REL( VB );

	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = new_size*12;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VB ) );

	vb_size = new_size;
}

void D3D11Pad::ExtendIndexBuffer( UINT new_size ) {
	REL( IB );

	D3D11_BUFFER_DESC bdesc;
	ZeroMemory( &bdesc, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = 2*new_size;
	bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DYNAMIC;

	HR( Dev->CreateBuffer( &bdesc, NULL, &IB ) );

	ib_size = new_size;
}

void D3D11Pad::Polyline( const oapi::IVECTOR2 *pt, int npt ) {
	if( !HasPen() )		return;

	int j;
	D3DXVECTOR3 *Vtx = new D3DXVECTOR3 [npt];
	for( j = 0; j < npt; j++ )
		Vtx[j].x = float(pt[j].x + origx),	Vtx[j].y = float(pt[j].y + origy);

	if( IsDashed() ) {
		Vtx[0].z = 0.0f;
		for( j = 1; j < npt; j++ ) {
			D3DXVECTOR3 d = Vtx[j] - Vtx[j-1];
			d.z = 0.0f;
			Vtx[j].z = Vtx[j-1].z + D3DXVec3Length( &d );
		}
		PSCB.Dash = 1;
	}
	else
		PSCB.Dash = 0;

//update vertex buffer and extend if needed
	if( npt > (int)vb_size )
		ExtendVertexBuffer( npt );
	D3D11_MAPPED_SUBRESOURCE SRes;
	ZeroMemory( &SRes, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, Vtx, (npt+1)*12 );
	iCtx->Unmap( VB, 0 );

//draw
	SetRParams();

	PSCB.COL = PenColor;
	iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
	iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

	iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );
	iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
	iCtx->VSSetShader( VS_Line, NULL, NULL );
	iCtx->PSSetShader( PS_Draw, NULL, NULL );

	iCtx->Draw( npt, 0 );

	delete [ ] Vtx;
}

void D3D11Pad::SetRParams( D3DXVECTOR4 *data ) {
	iCtx->IASetInputLayout( IL_D3DXVECTOR3 );
	iCtx->RSSetState( RS_CullNone_Solid );
	iCtx->OMSetBlendState( BS_SrcAlpha, D3DXCOLOR( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );

	if( data )	VSCB.Data = *data;
	iCtx->UpdateSubresource( cb_VS, 0, NULL, &VSCB.M, 0, 0 );
	iCtx->VSSetConstantBuffers( 0, 1, &cb_VS );
}

void D3D11Pad::Lines( D3DXVECTOR3 *pVert, int count ) {
	if( IsDashed() ) {
		pVert[0].z = 0.0f;
		for( UINT j = 1; j < ((UINT)count << 1); j++ ) {
			D3DXVECTOR3 d = pVert[j] - pVert[j-1];
			d.z = 0.0f;
			pVert[j].z = pVert[j-1].z + D3DXVec3Length( &d );
		}
		PSCB.Dash = 1;
	}
	else
		PSCB.Dash = 0;

	if( HasPen() ) {
		SetRParams();

		if( count*2 > (int)vb_size )
			ExtendVertexBuffer( count*2 );
		

	/*	ZeroMemory( &SRes, sizeof(SRes) );
		iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, pVert, count*2*12 );
		iCtx->Unmap( VB, 0 );*/

		D3D11_MAPPED_SUBRESOURCE SRes;
		ZeroMemory( &SRes, sizeof(SRes) );
		iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, pVert, count*2*12 );
		iCtx->Unmap( VB, 0 );

	//	iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINELIST );
		iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );

		PSCB.COL = PenColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->VSSetShader( VS_Line, NULL, NULL );
		iCtx->PSSetShader( PS_Draw, NULL, NULL );
		iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );

		iCtx->Draw( count*2, 0 );
	}
}

void D3D11Pad::Ellipse2( float l, float t, float r, float b ) {
	bool bLowRes = true;
	float
		w = r - l,
		h = b - t;

//	iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
	iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );
	if( w > 32.0f || h > 32.0f ) {
		bLowRes = false;
		iCtx->IASetVertexBuffers( 0, 1, &Circle_high, &SkpStride, &VBOffset );
	}
	else
		iCtx->IASetVertexBuffers( 0, 1, &Circle_low, &SkpStride, &VBOffset );

	iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
	iCtx->PSSetShader( PS_Draw, NULL, NULL );

	SetRParams( &D3DXVECTOR4( l, t, w, h ) );

	PSCB.Dash = 0;
	if( HasBrush() ) {
		PSCB.COL = BrushColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );
		
		iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
		iCtx->PSSetShader( PS_Draw, NULL, NULL );

		if( bLowRes )
			iCtx->Draw( 15, 1 );
		else
			iCtx->Draw( 63, 1 );
	}

	if( IsDashed() )
		PSCB.Dash = 1;

	if( HasPen() ) {
		PSCB.COL = PenColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
		iCtx->PSSetShader( PS_Draw, NULL, NULL );

		if( bLowRes )
			iCtx->Draw( 16, 1 );
		else
			iCtx->Draw( 64, 1 );
	}
}

void D3D11Pad::Rectangle2( float l, float t, float r, float b ) {
	static WORD Idx[6] = { 0, 1, 2, 0, 2, 3 };

	static D3DVECTOR Vtx[5] = {
		{ 0.0f, 0.0f, 0.0f},
		{ 1.0f, 0.0f, 0.0f},
		{ 1.0f, 1.0f, 0.0f},
		{ 0.0f, 1.0f, 0.0f},
		{ 0.0f, 0.0f, 0.0f}
	};

	SetRParams( &D3DXVECTOR4( l, t, r-l, b-t ) );
	PSCB.Dash = 0;

	D3D11_MAPPED_SUBRESOURCE SRes;
	ZeroMemory( &SRes, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, Vtx, 5*12 );
	iCtx->Unmap( VB, 0 );

//	iCtx->OMSetRenderTargets( 1, TEX->GetRTV(), NULL );
	iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
	iCtx->VSSetShader( VS_Ellipse, NULL, NULL );
	iCtx->PSSetShader( PS_Draw, NULL, NULL );

	if( HasBrush() ) {
		ZeroMemory( &SRes, sizeof(SRes) );
		iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, Idx, 12 );
		iCtx->Unmap( IB, 0 );

		PSCB.COL = BrushColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
		iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

		iCtx->DrawIndexed( 6, 0, 0 );
	}

	if( HasPen() ) {
		PSCB.COL = PenColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_LINESTRIP );

		iCtx->Draw( 5, 0 );
	}
}

void D3D11Pad::Rectangle3( float x, float y, float w, float h, int width ) {
	static WORD o_idx[24] = { 0, 1, 2,  2, 1, 3,  2, 3, 4,  4, 3, 5,  4, 5, 6,  6, 5, 7,  6, 7, 0,  0, 7, 1 };
	static WORD f_idx[6]  = { 1, 5, 3,  1, 7, 5 };

	float i = float( width/2 );
	float u = float( width - int(i+1) );

	D3DVECTOR Vtx[8] = {
		{ -1.0f,  1.0f,  u },
		{ -1.0f,  1.0f, -i },
		{  1.0f,  1.0f,  u },
		{  1.0f,  1.0f, -i },
		{  1.0f, -1.0f,  u },
		{  1.0f, -1.0f, -i },
		{ -1.0f, -1.0f,  u },
		{ -1.0f, -1.0f, -i }
	};

	SetRParams( &D3DXVECTOR4( x, y, w*0.5f, h*0.5f ) );
	PSCB.Dash = 0;

	D3D11_MAPPED_SUBRESOURCE SRes;
	ZeroMemory( &SRes, sizeof(SRes) );
	iCtx->Map( VB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
	memcpy( SRes.pData, Vtx, 8*12 );
	iCtx->Unmap( VB, 0 );
	
	iCtx->IASetVertexBuffers( 0, 1, &VB, &SkpStride, &VBOffset );
	iCtx->VSSetShader( VS_WideRect, NULL, NULL );
	iCtx->PSSetShader( PS_Fill, NULL, NULL );

	if( HasBrush() ) {
		ZeroMemory( &SRes, sizeof(SRes) );
		iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, f_idx, 12 );
		iCtx->Unmap( IB, 0 );

		PSCB.COL = BrushColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
		iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

		iCtx->DrawIndexed( 6, 0, 0 );
	}

	if( HasPen() ) {
		ZeroMemory( &SRes, sizeof(SRes) );
		iCtx->Map( IB, 0, D3D11_MAP_WRITE_DISCARD, 0, &SRes );
		memcpy( SRes.pData, o_idx, 48 );
		iCtx->Unmap( IB, 0 );

		PSCB.COL = PenColor;
		iCtx->UpdateSubresource( cb_PS, 0, NULL, &PSCB, 0, 0 );
		iCtx->PSSetConstantBuffers( 0, 1, &cb_PS );

		iCtx->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
		iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );

		iCtx->DrawIndexed( 24, 0, 0 );
	}
}

int D3D11Pad::CheckTriangle( short x, const D3DXVECTOR3 *pt, const WORD *Idx, float hd, short npt, bool bSharp ) {
	WORD A = Idx[x];
	WORD B = Idx[mod( x-1, npt )];
	WORD C = Idx[mod( x+1, npt )];

	float bx = pt[B].x - pt[A].x;
	float by = pt[B].y - pt[A].y;
	float ax = pt[C].x - pt[A].x;
	float ay = pt[C].y - pt[A].y;

	if( (bx*ay - by*ax)*hd > 0 )	return 0;	// Check handiness

	float aa = ax*ax + ay*ay;			// dot(a,a)
	float ab = ax*bx + ay*by;			// dot(a,b)
	float bb = bx*bx + by*by;			// dot(b,b)

	float qw = fabs(ab) / sqrt(aa*bb);	// abs(cos(a,b))
	if( bSharp && qw > 0.9f )	return 0;	// Bad Ear
	if( qw > 0.9999f )	return 0;		// All three points are lined up
	
	float id = 1.0f / (aa * bb - ab * ab);

	for( int i = 0; i < npt; i++ ) {

		WORD P = Idx[i];

		if( P == B || P == A || P == C )	continue;

		float cx = pt[P].x - pt[A].x;
		float cy = pt[P].y - pt[A].y;
		float ac = ax*cx + ay*cy;	 if( ac < 0 )	continue;	
		float bc = bx*cx + by*cy;	 if( bc < 0 )	continue;
		float u  = (bb*ac - ab*bc) * id;
		float v  = (aa*bc - ab*ac) * id;

		// Check if the point is inside the triangle
		// NOTE: Having u+v slightly above 1.0 is a bad condition, should find a better ear.
		if( ( u > 0.0f ) && ( v > 0.0f ) && ( (u+v) < 1.0f ) )	return 0;
	}

	return 1; // It's an ear
}

int D3D11Pad::CreatePolyIndexList( const D3DXVECTOR3 *pt, short npt, WORD *Out ) {
	if( npt < 3 )
		return 0;
	if( npt == 3 ) {
		Out[0] = 0;
		Out[1] = 1;
		Out[2] = 2;
		return 3;
	}

	short idx = 0;		// Number of indices written in the output
	short x = npt-1;	// First ear to test is the last one in the list
	bool bSharp = true; // Avoid sharp ears
	
	// Build initial index list
	WORD *In = new WORD [npt];
	for( int i = 0; i < npt; i++ )	In[i] = i;
	float sum = 0;
	int k = npt-1;
	for( int i = 0; i < k; i++ )
		sum += (pt[i].x*pt[(i+1)%k].y - pt[(i+1)%k].x*pt[i].y);

	if( sum > 0 )		sum = 1.0;
	else				sum = -1.0;

	while( npt > 3 ) {
		switch( CheckTriangle( x, pt, In, sum, npt, bSharp ) ) {
			case 0:
			{
				x--; 
				if( x < 0 ) { // Restart
					if( !bSharp ) {
						delete [ ] In;
						return idx;
					}
					bSharp = false;
					x = npt-1;
				}
				break;
			}
			case 1: 
			{
				WORD a = Out[idx] = In[mod( x-1, npt )];	idx++;
				WORD b = Out[idx] = In[mod( x, npt )];		idx++;
				WORD c = Out[idx] = In[mod( x+1, npt )];	idx++;
				npt--;
				for( int i = x; i < npt; i++ ) In[i] = In[i+1];
				x = mod( x-1, npt );
				break;
			}
		}
	}

	Out[idx] = In[0]; idx++;
	Out[idx] = In[1]; idx++;
	Out[idx] = In[2]; idx++;
	delete [ ] In;
	return idx;
}

HDC D3D11Pad::GetDC() {
/*	if( !hDC ) {
		SURFHANDLE surf = this->GetSurface();
		hDC = TM->GetSurfaceDC( (Texture*)surf );
	}
	return hDC;*/
	return NULL;
}
/*
void D3D11Pad::RelDC() {
	if( hDC ) {
		SURFHANDLE surf = this->GetSurface();
		TM->ReleaseSurfaceDC( hDC, (Texture*)surf );
		hDC = NULL;
	}
}
*/
//==========================================================================
//								D3D11Font
//==========================================================================

D3D11PadFont::D3D11PadFont( int height, bool prop, const char *face, Style style, int orientation ) : oapi::Font( height, prop, face, style, orientation ) {
	fonts_allocated++;

	char *def_fixedface = "Courier New";
	char *def_sansface = "Arial";
	char *def_serifface = "Times New Roman";

	if( face[0] != '*' ) {
		if( !_stricmp( face, "fixed" ) )
			face = def_fixedface;
		else if( !_stricmp( face, "sans" ) )
			face = def_sansface;
		else if( !_stricmp( face, "serif" ) )
			face = def_serifface;
		else if( _stricmp( face, def_fixedface ) && _stricmp( face, def_sansface ) && _stricmp( face, def_serifface ) )
			face = (prop ? def_sansface : def_fixedface);
	}
	else
		face++;

	pFont = NULL;
	hFont = NULL;
	bDelete = false;

	if( orientation )	rotation = float(orientation)*0.1f;
	else				rotation = 0.0f;

	// Browse cache ---------------------------------------------------
	//

	for( int j = 0; j < nfcache; j++ ) {
		if( fcache[j].height != height )
			continue;
		if( fcache[j].style != style )
			continue;
		if( fcache[j].prop != prop )
			continue;
		if( _stricmp( fcache[j].face, face ) )
			continue;
		pFont = fcache[j].pFont;
		if( !orientation )
			hFont = fcache[j].hFont;
	}

	int weight = (style & BOLD ? FW_BOLD : FW_NORMAL);
	DWORD italic = (style & ITALIC ? TRUE : FALSE);
	DWORD underline = (style & UNDERLINE ? TRUE : FALSE);

	// Create Windows GDI Font --------------------------
	//
	if( !hFont ) {
		hFont = CreateFontA( height, 0, orientation, orientation, weight, italic, underline, 0, 0, 3, 2, 3, 49, face );
		if( !hFont ) {
			face = (prop ? def_sansface : def_fixedface);
			hFont = CreateFontA( height, 0, orientation, orientation, weight, italic, underline, 0, 0, 3, 2, 1, 49, face );
		}
		if( orientation )
			bDelete = true;
	}

	// Create Direct3D accelerated font ------------------
	//
	if( !pFont ) {
		HFONT hNew = CreateFontA( height, 0, 0, 0, weight, italic, underline, 0, 0, 3, 2, 3, 49, face );

		pFont = new D3D11Text();
		pFont->Init( hNew, 255 );

		DeleteObject( hNew );

		pFont->SetRotation( rotation );

		if( nfcache > 120 )
			oapiWriteLog( "cache is full" );

		// Fill the cache --------------------------------
		//

		if( orientation )	fcache[nfcache].hFont = NULL;	// Do not cache rotated hFont for Windows GDI
		else				fcache[nfcache].hFont = hFont;

		fcache[nfcache].pFont = pFont;
		fcache[nfcache].height = height;
		fcache[nfcache].style = style;
		fcache[nfcache].prop = prop;
		strcpy_s( fcache[nfcache].face, 32, face );
		nfcache++;
	}
}

D3D11PadFont::~D3D11PadFont() {
	if( pFont )
		pFont->SetRotation( 0.0f );
	fonts_allocated--;
	if( hFont && bDelete )
		DeleteObject( hFont );
}

//==========================================================================
//								D3D11Pen
//==========================================================================

D3D11PadPen::D3D11PadPen( int s, int w, DWORD col ) : oapi::Pen( style, width, col )
{
	pens_allocated++;

	switch( s ) {
	case 0:
		style = PS_NULL;
		break;
	case 2:
		style = PS_DOT;
		break;
	default:
		style = PS_SOLID;
		break;
	}

	width = w;
	if( width < 1 )
		width = 1;
	hPen = CreatePen( style, width, COLORREF( col & 0xFFFFFF ) );
	if( !(col & 0xFF000000) )
		col |= 0xFF000000;
	fcolor = D3DXCOLOR( col );
	D3DXCOLORSWAP( &fcolor );
}

D3D11PadPen::~D3D11PadPen() {
	pens_allocated--;
	DeleteObject( hPen );
}

//==========================================================================
//								D3D11Brush
//==========================================================================

D3D11PadBrush::D3D11PadBrush( DWORD col ) : oapi::Brush( col )
{
	brushes_allocated++;

	hBrush = CreateSolidBrush( COLORREF( col & 0xFFFFFF ) );
	if( !(col & 0xFF000000) )
		col |= 0xFF000000;
	fcolor = D3DXCOLOR( col );
	D3DXCOLORSWAP( &fcolor );
}

D3D11PadBrush::~D3D11PadBrush() {
	brushes_allocated--;
	DeleteObject( hBrush );
}